package com.drerp.RH;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.PrimaryKeyJoinColumn;

import com.drerp.RH.DAO.ColaboradorDAO;
import com.drerp.RH.DAO.HorasTrabalhoDAO;
import com.drerp.RH.carreira.CondicaoPlanoCarreira;
import com.drerp.RH.carreira.RegraPlanoCarreira;
import com.drerp.integracao.Agenda;
import com.drerp.integracao.financeiro.Financeiro;
import com.drerp.integracao.financeiro.Imposto;

@Entity
@PrimaryKeyJoinColumn(name="usuario_id")
public class Colaborador extends Usuario {
	public Colaborador() {
		super();
		this.ativo = true;
		this.dataContrato = Calendar.getInstance();
	}
	
	public Colaborador(String nome) {
		super(nome);
		this.ativo = true;
		this.dataContrato = Calendar.getInstance();
	}
	
	private String cpf;
	private String rg_numero;
	private String rg_emissor;
	private String rg_uf;
	private String ctps_numero;
	private int ctps_serie;
	private String ctps_uf;
	
	@ManyToOne
	private Cargo cargo;
	
	@OneToMany(cascade=CascadeType.ALL, mappedBy="colaborador")
	private List<HorasTrabalho> horasTrabalhadas;
	
	private Calendar ultimoAcesso;
	
	private Calendar dataContrato;
	private Calendar dataAfastamento;
	private Calendar dataAvisoPrevio;
	private String causaAfastamento;
	
	private Boolean ativo;
	
	public String getCpf() {
		return cpf;
	}

	public void setCpf(String cpf) {
		this.cpf = cpf;
	}

	public String getCtps_numero() {
		return ctps_numero;
	}

	public void setCtps_numero(String ctps_numero) {
		this.ctps_numero = ctps_numero;
	}

	public int getCtps_serie() {
		return ctps_serie;
	}

	public void setCtps_serie(int ctps_serie) {
		this.ctps_serie = ctps_serie;
	}

	public String getCtps_uf() {
		return ctps_uf;
	}

	public void setCtps_uf(String ctps_uf) {
		this.ctps_uf = ctps_uf;
	}

	public String getRg_numero() {
		return rg_numero;
	}

	public void setRg_numero(String rg_numero) {
		this.rg_numero = rg_numero;
	}
	
	public String getRg_emissor() {
		return rg_emissor;
	}

	public void setRg_emissor(String rg_emissor) {
		this.rg_emissor = rg_emissor;
	}

	public String getRg_uf() {
		return rg_uf;
	}

	public void setRg_uf(String rg_uf) {
		this.rg_uf = rg_uf;
	}
	
	private ArrayList<Contrato> contrato;

	@OneToMany(fetch=FetchType.EAGER)
	public ArrayList<Contrato> getContratos() {
		return contrato;
	}
	
	public Contrato getContratos(int i) {
		return (Contrato) contrato.get(i);
	}
	
	public Iterator<Contrato> contratosIterator() {
		return contrato.iterator();
	}
	
	public boolean isContratosEmpty() {
		return contrato.isEmpty();
	}
	
	public boolean containsContratos(Contrato contrato) {
		return this.contrato.contains(contrato);
	}
	
	public boolean containsAllContratos(Collection<? extends Contrato> contratos) {
		return contrato.containsAll(contratos);
	}
	
	public int contratosSize() {
		return contrato.size();
	}
	
	public Contrato[] contratosToArray() {
		return contrato.toArray(new Contrato[contrato.size()]);
	}
	
	public <T extends Contrato> T[] contratosToArray(T[] contratos) {
		return (T[]) contrato.toArray(contratos);
	}
	/**
	 * Inserts the specified element at the specified position in this list (optional operation)
	 */
	public void addContratos(int index, Contrato contrato) {
		this.contrato.add(index, contrato);
	}
	/**
	 * Appends the specified element to the end of this list (optional operation).
	 */
	public boolean addContratos(Contrato contrato) {
		return this.contrato.add(contrato);
	}
	/**
	 * Removes the element at the specified position in this list (optional operation).
	 */
	public Object removeContratos(int index) {
		return contrato.remove(index);
	}
	/**
	 * Removes the first occurrence in this list of the specified element  (optional operation).
	 */
	public boolean removeContratos(Contrato contrato) {
		return this.contrato.remove(contrato);
	}
	/**
	 * Removes all of the elements from this list (optional operation).
	 */
	public void clearContratos() {
		contrato.clear();
	}
	/**
	 * Setter of the property <tt>contratos</tt>
	 */
	public void setContratos(ArrayList<Contrato> contratos) {
		contrato = contratos;
	}
	
	public Cargo getCargo() {
		return cargo;
	}
	
	public void setCargo(Cargo cargo) {
		this.cargo = cargo;
	}
	
	public Calendar getUltimoAcesso() {
		return ultimoAcesso;
	}

	public void setUltimoAcesso(Calendar ultimoAcesso) {
		this.ultimoAcesso = ultimoAcesso;
	}
	
	public Calendar getDataContrato() {
		return dataContrato;
	}
	
	public void setDataContrato(Calendar dataContrato) {
		this.dataContrato = dataContrato;
	}

	public Calendar getDataAfastamento() {
		return dataAfastamento;
	}

	public void setDataAfastamento(Calendar dataAfastamento) {
		this.dataAfastamento = dataAfastamento;
	}

	public Calendar getDataAvisoPrevio() {
		return dataAvisoPrevio;
	}

	public void setDataAvisoPrevio(Calendar dataAvisoPrevio) {
		this.dataAvisoPrevio = dataAvisoPrevio;
	}

	public String getCausaAfastamento() {
		return causaAfastamento;
	}

	public void setCausaAfastamento(String causaAfastamento) {
		this.causaAfastamento = causaAfastamento;
	}
	

	public Boolean getAtivo() {
		return ativo;
	}

	public void setAtivo(Boolean ativo) {
		this.ativo = ativo;
	}

	public List<HorasTrabalho> getHorasTrabalho() {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		return colabDAO.encontrarHorasTrabalhadasPorColaborador(this);
	}
	
	public long contarHorasTrabalhadas() {
		ColaboradorDAO colaboradorDAO = new ColaboradorDAO();
		List<HorasTrabalho> horasList = colaboradorDAO.encontrarHorasTrabalhadasPorColaborador(this);
		
		long somaHoras = 0;
		for (HorasTrabalho hora : horasList) {
			somaHoras += hora.getDataSaida().getTimeInMillis() - hora.getDataEntrada().getTimeInMillis();
		}
		
		return somaHoras/(1000*60*60);
	}
	
	public long contarHorasTrabalhadasNoMesPassado() {
		ColaboradorDAO colaboradorDAO = new ColaboradorDAO();
		
		int ano = Calendar.getInstance().get(Calendar.YEAR);
		int mes = Calendar.getInstance().get(Calendar.MONTH);
		
		Calendar inicio = new GregorianCalendar(ano, mes, 1);
		Calendar fim = new GregorianCalendar(ano, mes + 1, 1);
		
		List<HorasTrabalho> horasList = colaboradorDAO.encontrarHorasTrabalhadasPorColaboradorEntreDatas(this, inicio, fim);
		
		long somaHoras = 0;
		for (HorasTrabalho hora : horasList) {
			somaHoras += hora.getDataSaida().getTimeInMillis() - hora.getDataEntrada().getTimeInMillis();
		}
		
		return somaHoras/(1000*60*60);
	}
	
	public long contarHorasTrabalhadasNoMes(int mes, int ano) {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		
		Calendar inicio = new GregorianCalendar(ano, mes, 1);
		Calendar fim = new GregorianCalendar(ano, mes + 1, 1);
		
		List<HorasTrabalho> horasList = colabDAO.encontrarHorasTrabalhadasPorColaboradorEntreDatas(this, inicio, fim);
		
		long somaHoras = 0;
		for (HorasTrabalho hora : horasList) {
			somaHoras += hora.getDataSaida().getTimeInMillis() - hora.getDataEntrada().getTimeInMillis();
		}
		
		return somaHoras/(1000*60*60);
	}

	public void setHorasTrabalhadas(List<HorasTrabalho> horasTrabalhadas) {
		this.horasTrabalhadas = horasTrabalhadas;
	}
	
	public void addHorasTrabalhadas(HorasTrabalho horasTrabalho) {
		if (horasTrabalho.getColaborador() == null) {
			horasTrabalho.setColaborador(this);
		}
		
		HorasTrabalhoDAO horasDao = new HorasTrabalhoDAO();
		horasDao.save(horasTrabalho);
	}
	
	public void registrarSaida(){
		HorasTrabalho horas = new HorasTrabalho(this, ultimoAcesso, new GregorianCalendar());
		addHorasTrabalhadas(horas);
	}
	
	public int getAnosContratoTotal() {
		Calendar hoje = Calendar.getInstance();
		Calendar inicio = (Calendar)getDataContrato();
		int anos = 0;
		
		while (inicio.before(hoje)) {
			inicio.add(Calendar.YEAR, 1);
			anos += 1;
		}
		
		return anos;
	}
	
	public void salvarMudancas() {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		colabDAO.save(this);
	}
	
	public Double aplicaRegraPlanoCarreira(RegraPlanoCarreira regra) {
		Double salarioBase = getCargo().getSalario_base();
		Double adicional = 0.0;
		
		CondicaoPlanoCarreira cond = regra.getCondicao();
		
		long valor = 0 ;
		switch (cond.getMetrica().getCampoAfetado()) {
		case CONSULTAS_REALIZADAS:
			valor = Agenda.getConsultasRealizadas(getUsuario_id());
			break;
		case HORAS_TRABALHADAS:
			valor = this.contarHorasTrabalhadas();
			break;
		case TEMPO_CONTRATO:
			valor = this.getAnosContratoTotal();
			break;
		}
		
		boolean aplica = false;
		switch (cond.getComparador()) {
		case MAIOR_QUE:
			if (valor >= cond.getValor()) aplica = true;
			break;
		case MENOR_QUE:
			if (valor <= cond.getValor()) aplica = true;
			break;
		case MULTIPLO_DE:
			if (valor % cond.getValor() == 0) aplica = true;
			break;
		}
		
		if (aplica) {
			switch (regra.getAcao().getTipo()) {
			case SOMA:
				adicional = (double) regra.getAcao().getValor();
				break;
			case MULTIPLICA:
				adicional = salarioBase * (regra.getAcao().getValor() - 1);
				break;
			}
		}
		
		return adicional;
	}
	
	public Double calcularSalario() {
		Double salarioBruto = getCargo().getSalario_base();
		
		List<RegraPlanoCarreira> regrasPlanoCarreira = RegraPlanoCarreira.getRegrasParaTodos();
		regrasPlanoCarreira.addAll(RegraPlanoCarreira.getRegrasPorCargo(getCargo()));
		regrasPlanoCarreira.addAll(RegraPlanoCarreira.getRegrasPorColaborador(this));
		
		Double beneficios = 0.0;
		for (RegraPlanoCarreira regra : regrasPlanoCarreira) {
			beneficios += aplicaRegraPlanoCarreira(regra);
		}
		
		salarioBruto += beneficios;
		
		List<Imposto> impostos = Financeiro.aplicaImpostos(salarioBruto);
		
		Double descontos = 0.0;
		for (Imposto imposto : impostos) {
			descontos += salarioBruto * (imposto.getAliquota() / 100);
		}
		
		return salarioBruto - descontos;
	}

	public static List<Colaborador> encontraTodosColaboradores() {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		List lista = colabDAO.findAll(Colaborador.class);
		return lista;
	}
	
	public static List<Colaborador> encontraTodosColaboradoresAtivos() {
		ColaboradorDAO colabDao = new ColaboradorDAO();
		return colabDao.encontrarColaboresAtivos();
	}
	
	public static List<Colaborador> encontraTodosColaboradoresAfastados() {
		ColaboradorDAO colabDao = new ColaboradorDAO();
		return colabDao.encontrarColaboresAfastados();
	}
	
	public static Colaborador encontrarColaboradorPorId(long id) {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		return colabDAO.findById(Colaborador.class, id);
	}
	
	public static List<Colaborador> encontrarColaboresPorCargo(Cargo cargo) {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		return colabDAO.encontrarColaboresPorCargo(cargo);
	}
	
	public static long contaColaboradoresPorCargo(Cargo cargo) {
		ColaboradorDAO colabDAO = new ColaboradorDAO();
		return colabDAO.contarColaboradoresPorCargo(cargo);
	}
}
